package com.dp.log;

import java.util.ArrayList;

import org.apache.log4j.Layout;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;


/**
 * Pattern layout that can be configure to truncate stack traces based on package name.
 * This can be used to hide the uninteresting container part of server traces.
 */
public class TraceFilterPatternLayout extends PatternLayout {
    private ArrayList endPackages = new ArrayList();

    public TraceFilterPatternLayout() {
    }

    public TraceFilterPatternLayout(String pattern) {
        super(pattern);
    }

    public boolean ignoresThrowable() {
        return false;
    }

    public String format(LoggingEvent event) {
        String formatted = super.format(event);
        String[] stackTrace = event.getThrowableStrRep();

        if (stackTrace == null) {
            return formatted;
        }

        StringBuffer sb = new StringBuffer(formatted);
        boolean matched = false;
        boolean mute = false;
        int muteCnt = 0;

        for (int i = 0; i < stackTrace.length; ++i) {
            String line = stackTrace[i];

            if (mute) {
                if (line.startsWith("Caused by")) {
                    mute = false;
                    matched = false;
                    appendMuteCnt(sb, muteCnt);
                } else {
                    ++muteCnt;
                }
            }

            if (!mute) {
                boolean lineMatches = matchesEndClass(line);

                if (matched && !lineMatches && !line.startsWith("\t...")) {
                    mute = true;
                    muteCnt = 1;

                    continue;
                }

                matched = lineMatches;
                sb.append(line);
                sb.append(Layout.LINE_SEP);
            }
        }

        if (mute && (muteCnt != 0)) {
            appendMuteCnt(sb, muteCnt);
        }

        return sb.toString();
    }

    private void appendMuteCnt(StringBuffer sb, int muteCnt) {
        sb.append("\t... ");
        sb.append(muteCnt);
        sb.append(" not shown");
        sb.append(Layout.LINE_SEP);
    }

    boolean matchesEndClass(String line) {
        int classStart = line.indexOf("at ") + 3;
        int classEnd = line.indexOf('(');

        if ((classStart == 2) /* indexOf returned -1 */ || (classEnd == -1) ||
                (classStart >= line.length()) || (classStart >= classEnd)) {
            return false;
        }

        String className = line.substring(classStart, classEnd);

        for (int i = 0; i < endPackages.size(); ++i)
            if (className.startsWith((String) endPackages.get(i))) {
                return true;
            }

        return false;
    }

    public void setTraceEndPackage(String pack) {
        endPackages.add(pack);
    }
}
